Skip to main content

配置管理

最佳实践思想

  1. 区分必选值与可选择
  2. 配置文件与选项解耦
  3. 避免复杂
  4. 简单化, 除了必选的值, 默认值应该是科学的, 合理的, 最佳实践的值
  5. 防御编程, 防止其他人乱改和运行时修改配置, 如果用户手一抖, 一秒钟填写成1分钟, 对不合理的配置直接panic
  6. 配置版本和应用对齐, 回滚应用时, 应用和配置文件应该一一对应, 一起回滚, 在配置中心或者k8s配置

最佳实践

proto + 配置语言(json/yaml/toml)

proto可以进行高亮 yaml编写配置就可以与proto进行解耦

演进

函数配置

防止在运行时修改DailOption配置 优点:

  1. 告诉你可选和必选值
  2. 函数内方便使用默认值 扩展: 如果要扩展, 必须新增函数, 例如DailDatabase等才可以扩展
type DailOption struct {
// dailOption, 小写, 意味着没有任何人有修改里面的值
f func(*dailOption)
}

func Dial(network, address string, options ...DailOption) (Conn, error){
// 默认值
do := dailOption {
dial: net.Dial,
}
// 如果没有传递options, 也不会造成影响
for _, option := range options {
option.f(&do)
}
}

或者:

type DailOption func(*dailOption)

func Dial(network, address string, options ...DailOption) (Conn, error){
do := dailOption {
dial: net.Dial,
}
for _, option := range options {
option(&do)
}
}

函数配置, 包含返回值

用于单元测试等, 需要切换参数配置时 改之前与改之后

type option func(f *Foo) option
func Verbosity(v int) option {
return func(f *Foo) option {
prev := f.verbostiy
f.verbostiy = v
return Verbosity(prev)
}
}

func DoSomethingVerbosity(foo *Foo, verbosity int) {
prev := foo.Option(pkg.Verbosity(verbostiy))
defer foo.Option(prev)
}

包含interface的函数配置

type GreeterClient interface {
SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*SayHelloReply, error)
}

type CallOption interface {
before(*callInfo) error
after(*callInfo)
}
// EmptyCallOption 不改变 CallCallOption
type EmptyCallOption struct{}

// 对原有的grpc.CallOption进行扩展
type TimeoutCallOption struct {
grpc.EmptyCallOption
Timeout time.Duration
}

其它配置的缺点

传统结构体(不加指针)

缺点: 不加指针是copy该结构体, 无法使用默认值, 以及0值和设定的值无法区分

func Dail(c Config) (cn Conn, err error)

配置文件解析

如果是把配置文件解析到go结构体, 那么会产生以下问题: 例如Redis, 如果你使用了配置文件映射到结构体, 那么如果别人使用的是redis.NewConn, 与你的配置文件冲突, 那么副作用是什么?

// config.yaml
server:
address: xxx

// struct
type Config struct {
Address string
}

func main() {
c := Config {Address: xxx}
// 正常使用
c := Redis.Config {
Address: c.Address
}
// 其它使用
r, _ := &redis.NewConn(c)
c.Addr = "xxx" // 副作用是什么?
}

参数扩展

这样写不知道有多少个的方法

func Dial(network, address, timeout string)
func DialDatabase(network, address, timeout, database string) (Conn, error)

函数可选参数

缺点: 参数不清晰, 如果填多个Config, 哪个生效呢? 有什么副作用?

func Dail(c ...Config) (cn Conn, err error)

指针默认配置

缺点: 使用nil来表示使用默认的值, 这个nil作为默认值不是特别好. nil不应该作为公共函数的参数

func Dail(c *Config) (cn Conn, err error)

func main() {
Dail(nil)
}